package com.basker.pisces.springcloud.feign.fallback;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.util.ClassUtils;

import com.basker.pisces.springcloud.feign.FeignClientBeanProcessor;
import com.basker.pisces.springcloud.feign.FeignClientFactoryBeanAccessor;
import com.basker.pisces.utils.ReflectionUtils;

import feign.hystrix.FallbackFactory;
import javassist.CannotCompileException;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

/**
 * 如果{@link FeignClient#fallback()}和{@link FeignClient#fallbackFactory()}都没有设置，则设置默认的{@link FallbackFactory}。
 *
 * <p>
 * 这个工厂类型是通过javassist动态创建的，因为feignclient的每个fallbackFactory都是一个唯一的class，这个class再通过反射设置给FeignClientFactoryBean的fullbackFactory字段。
 * <p>
 * fallbackFactory需要注册到{@link BeanFactory}中，这是feign要求的。
 *
 * @author hangwen
 */
public class FeignClientFallbackProcessor implements FeignClientBeanProcessor {

    @Override
    public void processFeignClientBean(Object feignClientFactoryBean, ConfigurableListableBeanFactory beanFactory) {
        if (feignClientFactoryBean == null) {
            return;
        }

        Class<?> fallbackType = (Class<?>) FeignClientFactoryBeanAccessor.getFieldValue(feignClientFactoryBean,
                "fallback");
        Class<?> fallbackFactoryType = (Class<?>) FeignClientFactoryBeanAccessor.getFieldValue(feignClientFactoryBean,
                "fallbackFactory");

        // 如果都没有配置fallback则启用默认的fallBack
        if (fallbackType.equals(void.class) && fallbackFactoryType.equals(void.class)) {
            Class<?> clientType = (Class<?>) FeignClientFactoryBeanAccessor.getFieldValue(feignClientFactoryBean,
                    "type");

            try {
                // 创建对应clientType的fallbakFactoryType
                fallbackFactoryType = getFallbakFactoryClass(clientType.getName());
                FeignClientFactoryBeanAccessor.setFieldValue(feignClientFactoryBean, "fallbackFactory",
                        fallbackFactoryType);

                // 将fallbackFactory实例注册到beanFactory,后续HystrixTargeter会从中获取fallbackFactory
                Object fallbackFactory = ReflectionUtils.createNewInstance(fallbackFactoryType);
                beanFactory.registerSingleton(fallbackFactoryType.getSimpleName(), fallbackFactory);
            } catch (CannotCompileException | NotFoundException e) {
                throw new RuntimeException(e);
            }
        }

    }

    private Class<?> getFallbakFactoryClass(String clientClassName) throws CannotCompileException, NotFoundException {
        String fallbakFactoryClassName = clientClassName + "$FallbackFactory";

        try {
            return ClassUtils.forName(fallbakFactoryClassName, null);
        } catch (ClassNotFoundException e) {

        }

        ClassPool pool = ClassPool.getDefault();
        // 创建类型
        CtClass fallbakFactoryClass = pool.makeClass(fallbakFactoryClassName);
        // 设置基类
        CtClass proxyFallbackFactory = this.getProxyFallbackFactoryCls(pool);
        fallbakFactoryClass.setSuperclass(proxyFallbackFactory);

        // 创建getClientClassName方法
        CtMethod getClientClassName = proxyFallbackFactory.getMethod("getClientClassName", "()Ljava/lang/String;");
        getClientClassName = new CtMethod(getClientClassName, fallbakFactoryClass, null);
        getClientClassName.setBody("{return \"" + clientClassName + "\";}");

        fallbakFactoryClass.addMethod(getClientClassName);

        // 生成class对象
        return fallbakFactoryClass.toClass();
    }

    private CtClass getProxyFallbackFactoryCls(ClassPool pool) throws NotFoundException {
        ClassClassPath path = new ClassClassPath(ProxyFallbackFactory.class);
        pool.appendClassPath(path);

        return pool.get(ProxyFallbackFactory.class.getName());
    }

}
